#include <sys/timerfd.h>
#include <unistd.h>
#include "TimerTask.h"
#include "TimerWheel.h"
#include "EventLoop.h"
#include "Logger.h"

TimerWheel::TimerWheel(EventLoop *loop)
    : _tick(0), _capacity(60), _wheel(_capacity),
      _loop(loop),
      _timerfd(CreateTimerFd()),
      _timer_channel(new Channel(loop, _timerfd))
{
    _timer_channel->SetReadCallback(std::bind(&TimerWheel::Ontime, this));
    _timer_channel->EnableRead();
}

// 1. 移除定时定时任务
void TimerWheel::RemoveTimer(uint64_t id)
{
    auto pos = _timers.find(id);
    if (pos != _timers.end())
    {
        _timers.erase(pos);
    }
}

// 2. 创建一个定时器文件描述符
int TimerWheel::CreateTimerFd()
{
    int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (timerfd < 0)
    {
        ERR_LOG("TIMERFD CREATE FAILED");
        abort();
    }

    struct itimerspec itime;
    // 第一次超时时间为1s后
    itime.it_value.tv_sec = 1;
    itime.it_value.tv_nsec = 0;

    // 第一次超时后，每次超时的间隔时间为1s
    itime.it_interval.tv_sec = 1;
    itime.it_interval.tv_nsec = 0;

    timerfd_settime(timerfd, 0, &itime, nullptr);

    return timerfd;
}

// 3. 读取定时器文件描述符
int TimerWheel::ReadTimeFd()
{
    uint64_t times;
    int ret = read(_timerfd, &times, 8);
    if (ret < 0)
    {
        ERR_LOG("READ TIMERFD FAILED");
        abort();
    }
    return times;
}

// 4. 执行时间轮里面的定时任务
void TimerWheel::RunTimerTask()
{
    _tick = (_tick + 1) % _capacity;
    _wheel[_tick].clear(); // 情况指定位置的数组，就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
}

// 5. 根据超时时间，执行定时任务
void TimerWheel::Ontime()
{
    int times = ReadTimeFd();
    for (int i = 0; i < times; i++)
    {
        RunTimerTask();
    }
}

// 6. 添加定时任务
void TimerWheel::TimerAddInLoop(uint64_t id, uint32_t delay, const TaskFunc &cb)
{
    // 1. 构建定时任务
    PtrTask pt(new TimerTask(id, delay, cb)); // PtrTask 引用计数 = 1 这里出了作用域，那么PtrTask的引用计数就为1了。那么最后RunTimerTask执行clear的之后，就直接析构任务了
    pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
    // 2. 将定时器任务加入到_wheel中
    int i = (_tick + delay) % _capacity;
    _wheel[i].push_back(pt); //  PtrTask引用计数 = 2
    // 3. 加入到时间轮的_timers里面
    auto pos = _timers.find(id);
    // if (pos != _timers.end())   // 这里错了
    if (pos == _timers.end())
    {
        _timers.insert({id, pt});
    }
}

// 7. 刷新/延迟定时任务
void TimerWheel::TimerRefreshInLoop(uint64_t id)
{
    //通过定时器对象的weak_ptr构造一个shared_ptr，再添加到时间轮中
    auto it = _timers.find(id);
    if (it == _timers.end())
        return;
    PtrTask pt = it->second.lock();
    // 将对应的pt加入到_wheel中
    int delay = pt->DelayTime();
    int i = (_tick + delay) % _capacity;
    _wheel[i].push_back(pt);
}

// 8.
void TimerWheel::TimerCancelInLoop(uint64_t id)
{
    auto pos = _timers.find(id);
    if (pos == _timers.end()) // 没有找到定时任务，无法刷新，无法延时
        return;
    PtrTask pt = pos->second.lock();
    if (pt) // 如果自己已经销毁，则可能为空
        pt->Cancel();
}

//定时器中有个_timers成员，定时器信息的操作有可能在多线程中进行(如主线程想添加给所有的连接添加一个定时任务)，因此需要考虑线程安全问题
//如果不想加锁，那就把对应定期的所有操作，都放到一个线程中进行
void TimerWheel::TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb)
{
    _loop->RunInLoop(std::bind(&TimerWheel::TimerAddInLoop, this, id, delay, cb));
}
void TimerWheel::TimerRefresh(uint64_t id)
{
    _loop->RunInLoop(std::bind(&TimerWheel::TimerRefreshInLoop, this, id));
}
void TimerWheel::TimerCancel(uint64_t id)
{
    _loop->RunInLoop(std::bind(&TimerWheel::TimerCancelInLoop, this, id));
}
bool TimerWheel::HasTimer(uint64_t id)
{
    auto it = _timers.find(id);
    if (it == _timers.end())
    {
        return false;
    }
    return true;
}